perf(graphql): bypass ExternalContextCreator for scalar ResolveField fast-path#3838
Open
ArielSafar wants to merge 1 commit intonestjs:masterfrom
Open
perf(graphql): bypass ExternalContextCreator for scalar ResolveField fast-path#3838ArielSafar wants to merge 1 commit intonestjs:masterfrom
ArielSafar wants to merge 1 commit intonestjs:masterfrom
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
@ResolveField()is commonly used for simple scalar transformations (e.g., converting database values like0/1intoboolean, mapping enum codes to display values, etc.). When such scalar field resolvers are executed on large lists (thousands of parent objects), applications can experience severe latency inflation and high CPU usage, often visible in profiling as heavyrunMicrotasksactivity and repeated internal lifecycle bookkeeping.This overhead is disproportionately high relative to the trivial work done in the resolver and can turn otherwise fast queries (data fetch completes quickly) into very slow GraphQL responses due to resolver invocation overhead within
@nestjs/graphql.What is the new behavior?
This PR introduces a performance optimization for scalar
@ResolveField()execution in@nestjs/graphqlby reducing per-invocation overhead in the common case.Specifically, when:
fieldResolverEnhancersare disabled for field resolvers (default for many production setups), andthe library now uses a fast-path execution strategy that avoids unnecessary promise/microtask scheduling and reduces repeated internal lifecycle bookkeeping. This significantly improves latency and CPU utilization for queries returning large lists where scalar field resolvers are invoked thousands of times.
Benchmarks / repro (included in tests) show materially improved execution time for large list responses with scalar
@ResolveField()mappings, while preserving identical resolver semantics.Does this PR introduce a breaking change?
Other information
@ResolveFieldin@nestjs/graphqland compares it against returning data directly without field resolvers.yarn,yarn build, then inbenchmark-servicerunnpm installand execute./benchmark-compare.sh(afterchmod +x). The script runs the benchmark against the published@nestjs/[email protected], then swaps in a locally built/optimized package and runs again, printing a comparison.@nestjs/graphql v13.2.4) the benchmark shows ~30% mean overhead for@ResolveField; after the optimization, the overhead is ~1% and the difference is not statistically significant (Welch’s t-test p ≈ 0.31).